package com.minisoft.utils;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.sql.Timestamp;
import java.util.*;

/**
 * 转发请求的拦截器
 * @Author: juesu
 * @Date: 2021/06/30/16:47
 */
@Slf4j
public class GateWayDispatcher implements HandlerInterceptor {

    private DispatchConfig dispatchConfig;

    public GateWayDispatcher(DispatchConfig dispatchConfig) {
        this.dispatchConfig = dispatchConfig;
    }

    public GateWayDispatcher() {
        this.dispatchConfig = new DispatchConfig();
    }
    /**
     * 增加映射配置
     * @param prefixPattern 请求路径的前缀，比如目标服务的路径是/customer/call,prefixPattern配置成/pool
     *                           那么前端访问和拦截的就是：/pool/customer/call
     * @param patterns 目标服务的路径
     * @param serviceUrl 目标服务地址
     * @param appid 网关的appid
     * @param secret 网关的密钥
     */
    public void addMappingConfig(String prefixPattern,String patterns,String serviceUrl,
                                 String appid,String secret) {
        dispatchConfig.addMappingConfig(prefixPattern,patterns,serviceUrl, appid, secret);
    }
    /**
     * 增加映射配置
     * @param patterns 目标服务的路径
     * @param serviceUrl 目标服务地址
     * @param appid 网关的appid
     * @param secret 网关的密钥
     */
    public void addMappingConfig(String patterns,String serviceUrl,
                                 String appid,String secret) {
        this.addMappingConfig(null,patterns,serviceUrl, appid, secret);
    }

    public void addMappingConfig(String patterns,String serviceUrl) {
        this.addMappingConfig(null,patterns,serviceUrl, null, null);
    }

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String uri = request.getRequestURI();
        //todo 以后可以加上会话控制
        //如果hander 是 HandlerMethod，说明有定义这个Controller，优先走本地的实现，否则就转发
        if (handler instanceof HandlerMethod) {
            return true;
        }
        //处理直接转发的请求
        String result = null;
        Map<String, String> headerMap = ServletUtil.getHeaderMap(request);
        if (ServletUtil.isGetMethod(request)) {
            Map<String, String> paramMap = ServletUtil.getParamMap(request);
            result = doGet(uri, paramMap,headerMap);
        } else if (ServletUtil.isPostMethod(request)) {
            //带上url上的参数
            String queryString = request.getQueryString();
            if (StrUtil.isNotBlank(queryString)) {
                uri = uri + "?" + URLUtil.encodeQuery(queryString);
            }
            String body = ServletUtil.getBody(request);
            result = doPost(uri, body,headerMap);
        } else {
            throw new HttpRequestMethodNotSupportedException(request.getMethod());
        }
        ServletUtil.write(response,result,"application/json;charset=UTF-8");
        return false;
    }

    /**
     * 通过Get方式请求网关服务
     * @param uri
     * @param params
     * @return
     */
    public String doGet(String uri,Map params) {
        return doGet(uri,params,null);
    }
    /**
     * 通过Get方式请求网关服务
     * @param uri
     * @param params
     * @param headerMap
     * @return
     */
    public String doGet(String uri,Map params,Map<String, String> headerMap) {
        String url = dispatchConfig.getUrl(uri);
        String body = params.toString();
        log.info("调用接口{}，参数为：{}",url,body);
        Map<String, String> signHeaderMap = getHeader(uri);
        if (headerMap != null) {
            signHeaderMap.putAll(headerMap);
        }
        String result = HttpUtil.createGet(url).headerMap(signHeaderMap, true).form(params).execute().body();
        log.info("接口返回：{}", result);
        return result;
    }

    /**
     * 通过Post方式请求网关服务，参数类型是Map
     * @param uri
     * @param formMap
     * @return
     */
    public String doPost(String uri,Map formMap) {
        return doPost(uri,formMap,null);
    }
    /**
     * 通过Post方式请求网关服务，参数类型是Map
     * @param uri
     * @param formMap
     * @param headerMap
     * @return
     */
    public String doPost(String uri,Map formMap,Map<String, String> headerMap) {
        String url = dispatchConfig.getUrl(uri);
        log.info("调用接口{}，参数为：{}",url,formMap);
        Map<String, String> signHeaderMap = getHeader(uri);
        if (headerMap != null) {
            signHeaderMap.putAll(headerMap);
        }
        String result = HttpUtil.createPost(url).headerMap(signHeaderMap, true).form(formMap).execute().body();
        log.info("接口返回：{}", result);
        return result;
    }

    /**
     * 通过Post方式请求网关服务，参数类型为请求体Body
     * @param uri
     * @param body
     * @return
     */
    public String doPost(String uri,String body) {
        return doPost(uri,body,null);
    }
    /**
     * 通过Post方式请求网关服务，参数类型为请求体Body
     * @param uri
     * @param body
     * @param headerMap
     * @return
     */
    public String doPost(String uri,String body,Map<String, String> headerMap) {
        String url = dispatchConfig.getUrl(uri);
        log.info("调用接口{}，参数为：{}",url,body);
        Map<String, String> signHeaderMap = getHeader(uri);
        if (headerMap != null) {
            signHeaderMap.putAll(headerMap);
        }
        String result = HttpUtil.createPost(url).headerMap(signHeaderMap, true).body(body).execute().body();
        log.info("接口返回：{}", result);
        return result;
    }
    /**
     * 生成网关调用参数
     * @return
     * @param uri
     */
    private Map<String, String> getHeader(String uri) {
        String appid = dispatchConfig.getAppid(uri);
        if (appid == null) {
            return new HashMap<>();
        }
        String appSecret = dispatchConfig.getSecret(uri);
        Timestamp ts = new Timestamp(System.currentTimeMillis());
        String timestamp = ts.toString();
        timestamp = String.valueOf(System.currentTimeMillis());
        //拼接签名appid+timestamp
        String stringToSign = "appid=" + appid + "&" + "timestamp=" + timestamp;

        // 计算HMAC-SHA1,并转换为16进制,得到最终签名sign
        byte[] key = appSecret.getBytes();
        HMac mac = new HMac(HmacAlgorithm.HmacSHA1, key);
        String sign = mac.digestHex(stringToSign);

        // 设置公共参数
        Map<String, String> headerMap = new HashMap<>();
        headerMap.put("appid", appid);
        headerMap.put("timestamp", timestamp);
        headerMap.put("sign", sign);
        return headerMap;
    }

    public String doSignPost(String uri,String body) {
        String url = getUrlSign(HttpMethod.POST.name(),uri,null);//加上签名的公共参数
        log.info("调用接口{}，参数为：{}",url,body);
        String result = HttpUtil.post(url,body);
        log.info("接口返回：{}", result);
        return result;
    }

    public String doSignGet(String uri,JSONObject params) {
        String url = getUrlSign(HttpMethod.GET.name(),uri,params);//加上签名的公共参数
        log.info("调用接口{}，参数为：{}",url,params);
        String result = new HttpRequest(UrlBuilder.ofHttpWithoutEncode(url)).form(params).execute().body();
//        String result = HttpUtil.get(url,params); //会对+号不转码
        log.info("接口返回：{}", result);
        return result;
    }

    /**
     * 返回拼接公共参数后的url
     * @param uri
     * @param params
     * @return
     */
    private String getUrlSign(String method,String uri, JSONObject params) {
        Map<String,Object> commonParams = new HashMap<>();
        commonParams.put("AccessKeyId",dispatchConfig.getAppid(uri));
        commonParams.put("Expires",60);
        commonParams.put("Timestamp",DateUtil.format(new Date(), DatePattern.UTC_PATTERN));
        Map<String,Object> signParams = new HashMap<>(commonParams);
        if (params != null) {
            signParams.putAll(params);
        }
        List<String> sortedKeys = ListUtil.toList(signParams.keySet());
        Collections.sort(sortedKeys);
        System.out.println(sortedKeys);
        StringBuilder paramsSb = new StringBuilder();
        for (String sortedKey : sortedKeys) {
            if (paramsSb.length() > 0) {
                paramsSb.append("&");
            }
            paramsSb.append(sortedKey).append("=").append(signParams.get(sortedKey));
//            paramsSb.append(sortedKey).append("=").append(URLUtil.encodeQuery(String.valueOf(signParams.get(sortedKey))));
        }
        String encodeParams = URLUtil.encodeQuery(paramsSb.toString());
        String api = dispatchConfig.getUrl(uri).substring(8) + "?";
        StringBuilder urlParamSb = new StringBuilder();
        urlParamSb.append(method).append(api).append(encodeParams);
        byte[] digest = DigestUtil.hmac(HmacAlgorithm.HmacSHA1, dispatchConfig.getSecret(uri).getBytes())
                .digest(urlParamSb.toString());
        String signature  = Base64.encode(digest);
        commonParams.put("Signature",signature);
//        String url = dispatchConfig.getUrl(uri) + "?" + HttpUtil.toParams(commonParams, StandardCharsets.UTF_8,true);
        String url = HttpUtil.urlWithForm(dispatchConfig.getUrl(uri), commonParams, StandardCharsets.UTF_8, true);
        return url;
    }

    static final String SOAP_BODY = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:web=\"http://service.pi.market.gi/\">\n" +
            "   <soapenv:Header/>\n" +
            "   <soapenv:Body>\n" +
            "      <web:executeBus>\n" +
            "         <!--Optional:-->\n" +
            "         <businessCode>{}</businessCode>\n" +
            "         <merchantId>{}</merchantId>\n" +
            "         <reqTime>{}</reqTime>\n" +
            "         <opId>{}</opId>\n" +
            "         <data>{}</data>\n" +
            "      </web:executeBus>\n" +
            "   </soapenv:Body>\n" +
            "</soapenv:Envelope>";


    public String doSoap(String businessCode, String merchantId, String opId, Map<String,Object> data) {
        JSONObject json = new JSONObject(data,false);
        String reqTime = DateUtil.format(new Date(),"yyyyMMdd");
        String body = StrUtil.format(SOAP_BODY, businessCode, merchantId, reqTime, opId, json.toString());{}
        Map<String, String> headerMap = new HashMap<>();
        headerMap.put("Content-Type", "text/xml;charset=UTF-8");
        headerMap.put("SOAPAction","");
        String res = doPost("/mktservices/sappisvc?wsdl",body,headerMap);
        return res;
    }

    public String[] getPathPatterns() {
        return dispatchConfig.getPathPatterns();
    }

    public static void main(String[] args) {
        GateWayDispatcher gateWayDispatcher = new GateWayDispatcher();
//        gateWayDispatcher.addMappingConfig("/identityCheck/**,/agent/**,/saleMain/**,/customerRelation/**,/saleTeamSaleOrgRel/**," +
//                        "/customerReport/**,/followLogReport/**,/customerOpportunity/**,/saleConsultant/**,/reception/**",
//                "https://api.huafagroup.com/gf-customer-service-pro","gf-rs-rushi","1swaa4fffgr4");
//        gateWayDispatcher.addMappingConfig("/identityCheck/**,/agent/**,/saleMain/**,/customerRelation/**,/saleTeamSaleOrgRel/**," +
//                        "/customerReport/**,/followLogReport/**,/customerOpportunity/**,/saleConsultant/**,/reception/**",
//                "http://api.paasdev.huafagroup.com/gf-customer-center-service-test","gf-activity-admin","1234567");
//        gateWayDispatcher.addMappingConfig("/saleTeamSaleOrgRel/**","https://api.huafagroup.com/gf-customer-service-pre","marketing-pre","1q2w3e4r");

        JSONObject p = new JSONObject();
        gateWayDispatcher.addMappingConfig("/open-api/**","https://api.paasdev4.huafagroup.com/gf-activity-service-test","gf-rs-rushi","1234567");
        gateWayDispatcher.addMappingConfig("/modules/clientreport/**","https://apitest.huafagroup.com/gf-self-estension-test","gf-rs-rushi","1234567");
        gateWayDispatcher.addMappingConfig("/api/**","http://apitest.huafagroup.com/gf-market-permit-service-test","gf-activity-admin","1234567");
        gateWayDispatcher.addMappingConfig("/estateproject/**","http://apitest.huafagroup.com/gf-ydac-service-test","huafa_zyt","123456");
        gateWayDispatcher.addMappingConfig("/rate/**","https://api.huafagroup.com/gf-customer-service-pro","gf-customer-center","hu6dxe34bt");
        gateWayDispatcher.addMappingConfig("/reception/**","https://api.huafagroup.com/gf-customer-service-pro","gf-rs-rushi","1swaa4fffgr4");
        gateWayDispatcher.addMappingConfig("/accountMainInfo/**","https://api.huafagroup.com/gf-customer-service-pro","gfcommu-pro","5d3d44cd");
        String s = null;
        p.set("mobile","13825650999");
        p.set("saleTeamId","1000112");
        s = gateWayDispatcher.doGet("/reception/check", p);
//        p.set("id",1456);
//        s = gateWayDispatcher.doGet("/open-api/v1/open/promotion-activity/getById", p);

//        s = gateWayDispatcher.doPost("/accountMainInfo/register","{\"sourceCode\":\"SQXCX\",\"unionId\":\"oAzsdt-WCqPvVyOvdOZv69FSl9Ro\",\"openId\":\"oaM6X7QzavPEV8CvwaJ-d3Vt17IQ\",\"telephone\":\"13107686699\",\"appId\":\"wx58d954906d9a42c7\",\"name\":null,\"nickName\":\"微信用户\",\"gender\":2,\"pictureLink\":\"https://oss.huafagroup.com/hfzyt-miniprogram/hfhsh/image/default-avatar.png\"}");
//        p.set("key","gf@2024").set("rate",800);
//        p.set("bizKey","mobile2Check");
//        s = gateWayDispatcher.doGet("/rate/limiter/resetRate",p);
//        s = gateWayDispatcher.doGet("/rate/limiter/getInfo",p);
//        p.set("id","e59d70e07c9c4fc3988f4b8067303943");
//        s = gateWayDispatcher.doGet("/estateproject/info", p);
//
//        p.set("userKey","ceshi2").set("keyword","135896543192");
//        s = gateWayDispatcher.doPost("/modules/clientreport/customerList4WxWork",p.toString());
//
//        p.set("userKey","ceshi2").set("name","name").set("gender",0).set("mobile","135896543192");
//        s = gateWayDispatcher.doPost("/modules/clientreport/preReport4Fission",p.toString());


        //        p.set("realCityId","1119001");
//        String s = gateWayDispatcher.doPost("/identityCheck/proprietorRoleByProjectIdAndTelphone", p);
//        String s = gateWayDispatcher.doPost("/saleTeamSaleOrgRel/getTeamInfo", p.toString());

//        p.set("accessToken","eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIwMDAwMTEzOTUiLCJuYmYiOjE2OTEwNDc4NjYsImV4cCI6MTY5MTA1NTA2Nn0.n1yrxZsV2Is_zI4awjMa8MVUtmTuCh0ZyPZ6ZkERFKk");
//        s = gateWayDispatcher.doGet("/api/validUserLogin", p);
        System.out.println(s);
    }
}
